Skip to content

Scale Tukey alpha for long-duration signals#127

Open
yallup wants to merge 9 commits into
jim-devfrom
codex/tukey-roll-off
Open

Scale Tukey alpha for long-duration signals#127
yallup wants to merge 9 commits into
jim-devfrom
codex/tukey-roll-off

Conversation

@yallup

@yallup yallup commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • derive Jim's default Tukey alpha from a fixed 0.4-second roll-off using Bilby's alpha = 2 * roll_off / duration convention
  • preserve the existing alpha=0.2 behavior for four-second segments while reducing a 128-second BNS segment to alpha=0.00625
  • retain explicit alpha control, add custom roll_off control and validation, and document both parameterizations
  • cross-validate Jim's generated window against Bilby for 4-second and 128-second segments

Motivation

Jim previously used a fixed alpha=0.2, so the taper duration grew with the data segment. On a 128-second BNS segment this tapers 12.8 seconds on each side and can remove signal-rich late-inspiral data. A fixed roll-off time keeps the taper duration stable across BBH and long-duration BNS analyses.

The 0.4-second default is compatibility-preserving for Jim's standard four-second analyses while adopting Bilby's duration-scaled calculation.

Validation

  • 220 passed, 1 deselected — complete non-slow single-event unit suite plus Tukey cross-validation
  • 55 passed — focused data unit tests and Bilby cross-validation after the final type correction
  • Ruff check and format checks pass
  • Pyright reports no errors in the changed code; the full jim-dev run retains the pre-existing BlackJAX live_covariance environment error

Closes #15
Partially addresses #23

Summary by CodeRabbit

  • New Features
    • Added time-domain Tukey windowing with a documented default roll-off of 0.4 s per side.
    • Windowing can be set via alpha or roll_off (mutually exclusive), with roll-off scaling to the data duration.
  • Documentation
    • Updated the data guide with a “Time-domain windowing” section, including conventions and usage examples.
  • Bug Fixes
    • Improved validation of window parameters and added clearer behaviour when frequency-domain data are already produced (window changes are rejected).
  • Tests
    • Expanded unit tests for window values and error cases, plus cross-validation against bilby when available.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@thomasckng, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef3cfa90-179e-4c3c-b436-b1bc64cdddb0

📥 Commits

Reviewing files that changed from the base of the PR and between 233bd83 and 5b83292.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • docs/guides/data.md
  • src/jimgw/core/single_event/data.py
  • tests/cross_validation/test_tukey_window.py
  • tests/unit/core/single_event/test_data.py

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds duration-based Tukey window configuration, explicit parameter validation, Fourier-domain state tracking, PSD behaviour coverage, documentation, and bilby cross-validation.

Changes

Time-domain Tukey windowing

Layer / File(s) Summary
Duration-based window resolution
src/jimgw/core/single_event/data.py
Tukey windows use a 0.4-second default roll-off, support either alpha or roll_off, apply during FFT, and track fixed frequency-domain data.
Window behaviour and state validation
tests/unit/core/single_event/test_data.py
Tests cover window values, duration scaling, invalid parameters, fixed frequency-domain state, construction validation, and Welch PSD behaviour.
Bilby cross-validation
tests/cross_validation/test_tukey_window.py
Cross-validation compares Jim and bilby Tukey windows for four- and 128-second segments when bilby is available.
Windowing guide
docs/guides/data.md
Documents time-domain windowing, roll-off and alpha constraints, frequency-domain restrictions, and configuration examples.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Data
  participant TukeyWindow
  participant FourierTransform
  Data->>TukeyWindow: Resolve window parameters
  Data->>FourierTransform: Transform windowed time-domain data
  FourierTransform-->>Data: Store fixed frequency-domain data
Loading

Suggested reviewers: ssl32081, thomasckng

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarises the main change: Tukey alpha scaling for longer signals.
Linked Issues check ✅ Passed The PR exposes time-domain window alpha and adds the related validation and documentation requested by issue #15.
Out of Scope Changes check ✅ Passed The changes stay within the Tukey windowing work and related tests/docs, with no unrelated code introduced.
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/tukey-roll-off

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@yallup
yallup marked this pull request as ready for review July 16, 2026 11:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/cross_validation/test_tukey_window.py (2)

23-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add type annotations to the test function.

Adding type hints for the parameter and return type improves static analysis and maintainability.

💡 Proposed refactor
 `@pytest.mark.parametrize`("duration", [4, 128])
-def test_default_tukey_window_matches_bilby(duration):
+def test_default_tukey_window_matches_bilby(duration: int) -> None:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/cross_validation/test_tukey_window.py` around lines 23 - 24, Add a type
annotation for the duration parameter and annotate
test_default_tukey_window_matches_bilby with its return type, preserving the
existing parametrization and test behavior.

7-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Simplify conditional dependency skipping with Pytest.

The manual try/except block and pytestmark can be replaced with pytest.importorskip(), which is Pytest's idiomatic way to handle missing test dependencies. It natively skips the module and can also retain the helpful installation instruction that was previously being swallowed by the except block.

♻️ Proposed refactor
-from tests.utils import check_bilby_available
-
-try:
-    check_bilby_available()
-    import bilby
-
-    BILBY_AVAILABLE = True
-except ImportError:
-    BILBY_AVAILABLE = False
-
-pytestmark = pytest.mark.skipif(
-    not BILBY_AVAILABLE,
-    reason="bilby required for cross-validation tests",
-)
+pytest.importorskip(
+    "bilby",
+    reason="cross_validation tests require bilby. Install with: pip install bilby",
+)
+import bilby

(Note: Explicit import bilby is preserved after the skip check to ensure static type checkers like Pyright can still correctly resolve the module).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/cross_validation/test_tukey_window.py` around lines 7 - 20, Replace the
manual bilby availability check and module-level pytestmark with
pytest.importorskip(), preserving the check_bilby_available() installation
guidance before the skip and retaining the explicit bilby import afterward for
static type checking.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/cross_validation/test_tukey_window.py`:
- Around line 23-24: Add a type annotation for the duration parameter and
annotate test_default_tukey_window_matches_bilby with its return type,
preserving the existing parametrization and test behavior.
- Around line 7-20: Replace the manual bilby availability check and module-level
pytestmark with pytest.importorskip(), preserving the check_bilby_available()
installation guidance before the skip and retaining the explicit bilby import
afterward for static type checking.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 159b9711-49d1-47ef-8808-1f1c80b9f034

📥 Commits

Reviewing files that changed from the base of the PR and between acaa34b and 1fb61e8.

📒 Files selected for processing (4)
  • docs/guides/data.md
  • src/jimgw/core/single_event/data.py
  • tests/cross_validation/test_tukey_window.py
  • tests/unit/core/single_event/test_data.py

@thomasckng
thomasckng requested a review from SSL32081 July 16, 2026 11:35
@thomasckng thomasckng added the enhancement New feature or request label Jul 16, 2026
@thomasckng thomasckng added this to the v0.5.0 milestone Jul 16, 2026
Comment thread src/jimgw/core/single_event/data.py Outdated
if roll_off < 0:
raise ValueError("roll_off must be non-negative")
duration = float(self.duration)
resolved_alpha = 0.0 if duration == 0 else 2 * roll_off / duration

@SSL32081 SSL32081 Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check for duration == 0 seems a bit extra; I would suggest removing it.
Such data would have thrown all sorts of errors early on.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This guard is kept because Data() is used as an empty placeholder during detector construction and reset. Data.__init__() calls set_tukey_window(), so removing the guard would divide by zero before strain data is assigned. For the empty placeholder, alpha=0 produces an empty window.

Comment thread src/jimgw/core/single_event/data.py Outdated
Comment on lines +200 to +201
if roll_off < 0:
raise ValueError("roll_off must be non-negative")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, a negative alpha will not lead to any errors and will return an identity window. Also, if we are checking roll_off, shouldn't we be checking alpha as well?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added validation for alpha

@thomasckng
thomasckng force-pushed the codex/tukey-roll-off branch from 208dce5 to 233bd83 Compare July 20, 2026 14:57
@thomasckng
thomasckng requested a review from SSL32081 July 20, 2026 21:10
@thomasckng

Copy link
Copy Markdown
Member

@SSL32081 I have added some more changes.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/jimgw/core/single_event/data.py (2)

254-279: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the new RuntimeError in the docstring.

fft() can now raise RuntimeError once frequency-domain data are fixed (line 265-267), but the docstring has no Raises: section, unlike the sibling set_tukey_window() which documents its exceptions thoroughly.

📝 Proposed fix
         Args:
             window: Window function to apply to the data before FFT (default: None).
+
+        Raises:
+            RuntimeError: If a custom window is supplied after frequency-domain
+                data have already been fixed.
         """
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/jimgw/core/single_event/data.py` around lines 254 - 279, Update the fft
method docstring to add a Raises section documenting the RuntimeError raised
when a window is supplied after frequency-domain data has been fixed. Keep the
existing argument and behavior documentation unchanged.

144-189: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a shape check for an explicit window.

delta_t, alpha, and roll_off all now get explicit range/finite validation, but a caller-supplied window (line 189, self.window = window) is never checked against td's length. A mismatched shape will only surface later as a cryptic broadcasting error inside fft() (self.td * window), instead of a clear message at construction time.

🛡️ Proposed fix
         if window is None:
             if self.is_empty:
                 # Empty Data instances are used as detector placeholders.
                 self.window = jnp.array([])
             else:
                 self.set_tukey_window()
         else:
+            if window.shape != self.td.shape:
+                raise ValueError(
+                    "window must have the same shape as td"
+                )
             self.window = window
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/jimgw/core/single_event/data.py` around lines 144 - 189, Validate an
explicitly supplied window in the __init__ method before assigning self.window:
for non-empty data, ensure its shape matches td’s shape or length and raise a
clear ValueError on mismatch. Preserve the existing empty-data behavior and
automatic Tukey-window initialization when window is None.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/guides/data.md`:
- Around line 110-111: Update the guidance around set_tukey_window() to describe
the materialisation boundary: it may be called any time before frequency-domain
data are materialised, rather than only “immediately” after creating or loading
data. Preserve the existing requirement that it must not be called once
frequency-domain data exist.

---

Nitpick comments:
In `@src/jimgw/core/single_event/data.py`:
- Around line 254-279: Update the fft method docstring to add a Raises section
documenting the RuntimeError raised when a window is supplied after
frequency-domain data has been fixed. Keep the existing argument and behavior
documentation unchanged.
- Around line 144-189: Validate an explicitly supplied window in the __init__
method before assigning self.window: for non-empty data, ensure its shape
matches td’s shape or length and raise a clear ValueError on mismatch. Preserve
the existing empty-data behavior and automatic Tukey-window initialization when
window is None.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0c381c69-1064-48ae-846a-900dd74c750d

📥 Commits

Reviewing files that changed from the base of the PR and between 233bd83 and 5b83292.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • docs/guides/data.md
  • src/jimgw/core/single_event/data.py
  • tests/cross_validation/test_tukey_window.py
  • tests/unit/core/single_event/test_data.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/cross_validation/test_tukey_window.py

Comment thread docs/guides/data.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants